/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package semmatch;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jdom.*;
import org.jdom.input.*;

/**
 *
 * @author cypry
 */
public class WebService {

    private String name;
    private List<Concept> inputs;
    private List<Concept> outputs;

    public WebService(String path, ClassHierarchy ch){
        name = (new File(path)).getName();
        name = name.substring(0, name.length() - 5);
        Map<String, Concept> types = new HashMap<String, Concept>();
        Map<String, List<String>> messages = new HashMap<String, List<String>>();
        List<String> inputMsg = new ArrayList<String>();
        List<String> outputMsg = new ArrayList<String>();

        SAXBuilder builder = new SAXBuilder();
        try{
            Document doc = builder.build(path);
            for(Object obj : doc.getRootElement().getChildren() ){
                Element elem = (Element)obj;
                if(elem.getName().equals("types")){
                    for(Object obj1 : elem.getChildren()){
                        Element elem1 = (Element)obj1;
                        if(elem1.getName().equals("schema")){
                            for(Object obj2 : elem1.getChildren()){
                                Element elem2 = (Element)obj2;
                                if(elem2.getName().equals("simpleType") ||
                                        elem2.getName().equals("complexType")){
                                            String type = null;
                                            String ref = null;
                                            for(Object obj3 : elem2.getAttributes()){
                                                Attribute attrib = (Attribute)obj3;
                                                if(attrib.getName().equals("name"))
                                                    type = attrib.getValue();
                                                else if(attrib.getName().equals("modelReference"))
                                                    ref = attrib.getValue();
                                            }
                                            String strConcept = ref.substring(ref.indexOf("#")+1);
                                            //System.out.println(type + " - " + strConcept);
                                            if(strConcept != null && strConcept.length() > 0){
                                                Concept cnc = ch.find(strConcept);
                                                if(cnc != null){
                                                    types.put(type, cnc);
                                                    //System.out.println("put");
                                                }else{
                                                    System.out.println("concept not found in ontologies " +
                                                            strConcept);
                                                }
                                            }
                                }
                            }
                        }
                    }
                }else if(elem.getName().equals("message")){
                    String msgName = elem.getAttributeValue("name");
                    List<String> parts = new ArrayList<String>();
                    for(Object obj1: elem.getChildren()){
                        Element elem1 = (Element)obj1;
                        if(elem1.getName().equals("part")){
                            String type = elem1.getAttributeValue("type");
                            if(type.contains(":")){
                                type = type.substring(type.indexOf(":")+1);
                            }
                            parts.add(type);
                        }
                    }
                    messages.put(msgName, parts);
                }else if(elem.getName().equals("portType")){
                    for(Object obj1: elem.getChildren()){
                        Element elem1 = (Element)obj1;
                        if(elem1.getName().equals("operation")){
                            for(Object obj2 : elem1.getChildren()){
                                Element elem2 = (Element)obj2;
                                if(elem2.getName().equals("input")){
                                    String message = elem2.getAttributeValue("message");
                                    if(message.contains(":")){
                                        message = message.substring(message.indexOf(":")+1);
                                    }
                                    inputMsg.add(message);
                                }else if(elem2.getName().equals("output")){
                                    String message = elem2.getAttributeValue("message");
                                    if(message.contains(":")){
                                        message = message.substring(message.indexOf(":")+1);
                                    }
                                    outputMsg.add(message);
                                }
                            }
                        }
                    }
                }
            }
        }catch(Exception e){
            System.err.println(e);
            System.exit(1);
        }
        inputs = new ArrayList<Concept>();
        for(String message : inputMsg){
            List<String> concepts = messages.get(message);
            for(String concept : concepts){
                Concept cnc = types.get(concept);
                if(cnc != null){
                    inputs.add(cnc);
                }else{
                    System.out.println("not found type " + concept);
                }
            }
        }
        outputs = new ArrayList<Concept>();
        for(String message : outputMsg){
            List<String> concepts = messages.get(message);
            for(String concept : concepts){
                Concept cnc = types.get(concept);
                if(cnc != null){
                    outputs.add(cnc);
                }else{
                    System.out.println("not found type " + concept);
                }
            }
        }
    }

    /**
     * @return the inputs
     */
    public List<Concept> getInputs() {
        return inputs;
    }

    /**
     * @return the outputs
     */
    public List<Concept> getOutputs() {
        return outputs;
    }

    private static double matchLists(List<Concept> domain, 
            List<Concept> codomain, ClassHierarchy ch){
        if(domain.size() > codomain.size())
            return matchLists(codomain, domain, ch);
        if(domain.size() == 0 || codomain.size() == 0){
            if(domain.size() == 0 && codomain.size() == 0){
                return 1.0;
            }else{
                return 0.0;
            }
        }
        //System.out.println("dom, codom : " + domain.size() + " " + codomain.size());
        double[][] mat = new double[domain.size()][codomain.size()];
        for(Concept c1 : domain){
            for(Concept c2: codomain){
                double m = ch.cm(c1, c2, Constants.HORIZONTAL_DEPTH);
                //System.out.println("match: " + c1.getId() + "; " + c2.getId() + " = " + m);
                mat[domain.indexOf(c1)][codomain.indexOf(c2)] = 1 - m;
            }
        }
        //System.out.println("apelam");
        //int[][] result = new HungarianAlgorithm().computeAssignments(mat);
        int[][] result = HungarianAlgorithm.hgAlgorithm(mat, "");
        //System.out.println("am apelat. result length = " + result.length);
        double avg = 0;
        for(int i=0; i<result.length; ++i){
            int i1 = result[i][0];
            int i2 = result[i][1];
            //System.out.println("i1=" + i1 + "; i2=" + i2 + "; mat[i1][i2]=" + mat[i1][i2]);
            avg += (1.0 - mat[i1][i2]);
        }
        avg /= result.length;
        return avg;
    }

    public double match(WebService other, ClassHierarchy ch){
        List<Concept> i1 = this.inputs;
        List<Concept> i2 = other.getInputs();
        List<Concept> o1 = this.outputs;
        List<Concept> o2 = other.getOutputs();
        double m1 = matchLists(i1, i2, ch);
        //System.out.println("Input match : " + m1);
        double m2 = matchLists(o1, o2, ch);
        //System.out.println("Output match : " + m2);
        return (m1 + m2)/2;
    }

    @Override
    public String toString(){
        StringBuffer str = new StringBuffer();
        str.append(name);
        str.append(":(");
        for(Concept inp : inputs){
            str.append(inp.getId() + ";");
        }
        if(inputs.size() > 0)
            str.delete(str.length()-1, str.length());
        str.append(")->(");
        for(Concept outp : outputs){
            str.append(outp.getId() + ";");
        }
        if(outputs.size() > 0)
        str.delete(str.length()-1, str.length());
        str.append(")");

        return new String(str);
    }
    
}
